IOS12+Swift4+Xcode10开发 - 4 元气天气APP

项目介绍

app5

需求分析

让用户查看当地天气、查询异地天气的时看到正能量文字,元气满满的开始新的一天。

本程序共分为三个页面:欢迎页面,天气预览界面,城市天气查询界面。

需求

天气预览界面:定位天气展示【1 获取当前经纬度位置 2 根据位置获取天气、城市信息 3将信息展示在视图】

城市天气查询界面:用户查询天气【1展示当前城市 2输入框输入查询城市的名称 3按下查询按钮-获取名称至页面1,销毁页面2 】

采用了MVC模式

model部分负责数据,我们构建天气类,属性包括:温度、城市、condition,以及计算属性——icon、words,他们需要根据condition属性来确定。比如晴天对于icon为小太阳、文字对于“天晴是你的心情”。

controller负责两个页面中的业务逻辑:从model中取数据,然后返回给view。

最终在view上展示所需天气及其文字信息。

遇到的困难

页面跳转传值

页面跳转传值部分。【功能:按页面1右上角按钮,跳转到页面二,页面二展示当前city;用户输入目标city,按查询后跳转回页面一,展示查询city的天气信息】

segue导航正向传值较容易,3步骤。1在prepare函数部分获取segue的identifier【转换箭头】,2使用as!强制转化,实例化该segue的destination页面vc,3使用实例化的vc进行传值。这样就可以在下一个页面使用本页面中的“所在城市”的信息了。

而反向传值就不同。如果采用control+drag方式,从页面二的button到页面一加上转换箭头,这样只会再重新生成新的页面一,定位到本地天气;而不是预想的回到原先页面,重新展示所搜索城市的信息。

所以需要自定义protocol,使用delegate实现导航的反向传值。

在页面二需要:3个步骤来自定义协议portocol,实现【按下查找城市按钮,获取输入的city值】的功能。

1自定义协议,安排协议中的方法(获取输入的city,传给页面一),2 声明协议属于页面二中的delegate变量 3规定在哪里触发协议中的事件。

在页面一种需要:3个步骤来实现协议,实现【获取页面二中city的值,并展示于本页面】的功能。

1遵守协议 2 实现协议中的方法(获取city值,根据值获取温度对象的所有信息,展示与本页面)3 第二个页面vc的事件函数委托delegate给self实现。

获取当前位置的天气

1 委托本页面,实现定位协议中的请求位置的方法,获得经纬度。(难点)

2 根据经纬度信息,使用Alamofire请求API来获取天气数据;

3 利用SwiftJSON解析生成数据;

4 给weather对象赋值;

5 再使用weather对象中的天气、文字信息更新页面。

使用了定位协议CLLocationManagerDelegate的方法。

1 遵守定位协议 2 实例化定位管理器CLLocationManager 3 实现协议中的方法(获取当前的经纬度)4locationManager委托本页面

当manager请求位置的时候,则系统自动调用manager方法,在该方法里获得经纬度。

知识点

protocol-delegate:类似java中的接口与实现

协议——工具间,协议中的方法——工具,实例化的对象——老板,本页面self——打工仔,委托——老板让打工仔干活

计算属性

model中的计算属性——compute属性 某个属性需要根据另一个属性来确定,我们定义其为计算属性

如根据天气情况属性——晴天,得到对应的icon和words:icon为小太阳、wors为“天晴是你的心情”。

第三方库的CocoaPods安装的使用

Alamofire:完成http请求天气数据

SwiftJSON:解析、生成数据

extension代码优化

目的:提高代码可读性。为日后重构代码打下了良好的基础。

场景:1 私有的辅助函数 ;2 遵守协议(实现某个协议的方法放到一个 extension 中);3 模型(Model用结构体, 使用extension 将 Model 的 属性 和 基于属性的计算计算属性分离 )

项目布局

UI设计

image-20190306215033210

页面一

创建项目的时候需要取消左右旋转

image-20190306194956681

背景图约束:

上下左右相对于View都是0

image-20190306201552299

背景图片填充方式为Aspect Fill拉伸

image-20190306201629780

中间图片

image-20190306203332315

Label 左边自适应

image-20190306203111728

页面二

StackView自适应页面的宽度:

选中StackView后control+drag至背景图

image-20190306212100086

image-20190306212158796

是的stackView:页面的比例为2:5

image-20190306212340645

错误:该页面无法到达,需要添加跳转image-20190306212714327

需要按下按钮,跳转至新的页面:选中按钮,control+drag 至第二页面,选择show(直接跳转)/Present modally(从下往上弹出),生成连接。

image-20190306214019632

image-20190306213738640

启动页面

同理自行设计

依赖管理工具CocoaPods

介绍

  • 什么是CocoaPods

CocoaPods是OS X和iOS下的一个第三类库管理工具,通过CocoaPods工具我们可以为项目添加被称为“Pods”的依赖库(这些类库必须是CocoaPods本身所支持的),并且可以轻松管理其版本。

  • CocoaPods的好处

1、在引入第三方库时它可以自动为我们完成各种各样的配置,包括配置编译阶段、连接器选项、甚至是ARC环境下的-fno-objc-arc配置等。

2、使用CocoaPods可以很方便地查找新的第三方库,这些类库是比较“标准的”,而不是网上随便找到的,这样可以让我们找到真正好用的类库。

  • 需要第三方库的时候,三个步骤:

0、安装cocoapos

1、command+N导入项目

2、自动创建podfile文件,在该文件写入pod ‘工具包名称’,点击instal下载

3、项目安装配置第三方库完成,点击r.xcworkspace文件进入项目

在CocoaPods中安装第三方库SVProgressHUD

  • 导入

image-20190306215230817

  • 写需要的第三方库,安装

image-20190306220337045

  • 安装配置完成。安装配置过功能包之后,打开项目都是使用Weather.xcworkspace文件

image-20190306235457431

image-20190306220549930

获取经纬度

请求用户给予权限的窗口未弹出,报如下错误

image-20190307002833641

原因是:弹框弹出时,需要给到一个描述给用户,说明为什么需要该信息,在info.plist中设置

image-20190307003416351

问题解决:

https://swift.gg/2017/02/13/requesting-permissions-core-location-tutorial/

API 使用网络请求功能包Alamofire完成http请求获取天气数据

https://study.163.com/course/courseLearn.htm?courseId=1208935831&share=1&shareId=1386037112#/learn/video?lessonId=1278405463&courseId=1208935831

API:应用程序接口(API:Application Program Interface)

https://openweathermap.org/api

选择合适的API

image-20190306235756780

根据经纬度获取天气需要的信息

image-20190307001350911

HTTP+字典Dictionary+JSON+第三方功能包SwiftyJSON

字典类型:键值对构成

let dict:[String:Any] = [“name”:”zhangsan”,”age”:18]

创建Weather模型+把获取到的数据显示到页面+计算属性初探

https://openweathermap.org/weather-conditions

代码优化-把功能块分离出来变成函数+extension

view连接controller+segue导航正向传值+三种as+delegate实现导航的反向传值

view连接controller

选择cocoaTouch class,创建controller,选择页面二,建立连接。

image-20190307125134236

segue导航正向传值

1、选中转换箭头,设置该转换的id

2、通过在segue的函数中的segue.identifier找到究竟是哪一个【转换箭头】

3、根据转换箭头,使用 sugue.destination 找到箭头的目的地页面

4、使用as!强制转换获取页面vc(实例化的对象)

5、利用vc页面,传值

自定义protocol和delegate实现导航的反向传值

页面二:selectCityController

  1. 自定义协议,安排了协议的方法
  2. 工具属于谁的——页面二中的delegate变量的
  3. 规定在哪里触发时间——按下查找城市按钮

页面一:viewController

  1. 遵守协议——领取工具
  2. 实现协议中规定的方法——去干活。原因:想要用协议的方法,获取里面的参数
  3. 委托——指导谁(vc)委托给本页面(self)的

转型——三种as

as 向上转型:安全,一般直接使用Int(3.2),所以不常用 3.2 as Int

as? 向下转型:需要判断 if let,若转换不了,得到nil

1
if let vc = segue.destination as? SelectCityController

as! 向下转型

1
let vc = segue.destination as! SelectCityController

image-20190307130456171

完成用户查询天气的功能

光标的出现和收起

1
2
3
4
5
6
7
8
9
10
11
12
13
14
    override func viewDidLoad() {
super.viewDidLoad()
currentCityLabel.text = currentCity
//打开本页面,输入框成为第一个响应者,光标显示在输入框
inputCity.becomeFirstResponder()
}

///该段代码放在退出页面处即可
// 让用户界面在主线程上进行——优先执行
// 经常用于使UI方面的操作提前执行,查询可以慢慢查,我们的键盘先收起来——用户体验更好
DispatchQueue.main.async {
// 3.收起键盘 让searchBar失去焦点(光标消失+软件盘收起)
self.inputCity.resignFirstResponder()
}

源码

1
2